/***************************************************************************
 *   oboe.controllers.AutoSignal                                           *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.controllers
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.MovieClip;
	import flash.display.SimpleButton;
	import flash.events.Event;
	import flash.text.TextField;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	
	import oboe.core.Controller;
	import oboe.core.Oboe;
	import oboe.core.SigSlot;
	import oboe.utils.Prototype;
	
	public class AutoSignal extends SigSlot
	{
		private var rules:Array;
		private var parent:SigSlot;
		private var autoPipe:Boolean;
		private var labelRule:RegExp = /lbl(.*)/s;
		
		public static var lastTarget:DisplayObject;
	
		// Asset name -> Signal name -> Parameters
		public static const rule_btnX_onClickX:Object = { regex : /btn(.*)/s, events : [["click", "onClick$1"]] };
		public static const rule_btnXY_onClickX_Y:Object = { regex : /btn([a-zA-Z]*)([0-9]+)/s, events : [["click", "onClick$1", "$2"]] };
		public static const rule_btnReturn_onClickReturnX:Object = { regex : /btnReturn/si, events : 
		[
			[
				"click", 
				function(a:AutoSignal):String{
					return "onReturnFrom" + getQualifiedClassName(a.parent).replace(/[^:]*::(.*)/i, "$1"); 
				}
			]
		]};
		
		public static const STDRULES:Array = [
			AutoSignal.rule_btnX_onClickX,
			AutoSignal.rule_btnXY_onClickX_Y,
			AutoSignal.rule_btnReturn_onClickReturnX
		];
		
		public function AutoSignal( rule:Array=null, autoPipe:Boolean=true, debugTraces:Boolean=true )
		{
			super( );
			if( rule == null ) rule = STDRULES;
			this.rules = rule;
			this.autoPipe = autoPipe;
			this.debugTraces = debugTraces;
		}
		
		public function slot_onAddToParent():void
		{
			parent = this.getSigSlotParent();
			if( !parent ) return;
			var ret:Array = [];
			parent.message( "_getDisplayObject", ret );
			if( !ret.length ) return;
			var asset:DisplayObject = ret[0];
			
			this.clips = new Dictionary();
			var events:Object = {};
			for each( var rule:Object in this.rules )
			{
				for each( var eventList:Array in rule.events )
				{
					events[ eventList[0] ] = eventList[0];
				}
			}
			var THIS:AutoSignal = this;
			var autos:Object = {};
			
			asset.addEventListener(Event.REMOVED, function(e:Event):void{
				if( e.target != asset ) return;
				ret = [];
				parent.message( "_getDisplayObject", ret );
				if( ret[0] != asset )
				{
					slot_onAddToParent();
				}
			});
			
			for each( var event:String in events )
			{
				asset.addEventListener( event, function( e:Event ):void{
					var p:DisplayObject = DisplayObject(e.target);
					while( p && p != asset && p.name.indexOf("instance") == 0 ){ p = p.parent; }
					if( !p ) return;
					var c:SigSlot = null;
					var tmp:DisplayObject = p;
					var lu:Dictionary = Oboe.controllerLookup;
					while( !c && tmp )
					{
						c = lu[ tmp ];
						tmp = tmp.parent;
					}
					if( c != parent ) return;
					lastTarget = p;

					for each( var rule:Object in rules ){
						var reg:RegExp = rule.regex;
						var ret:Object = reg.exec( p.name );
						if( ret == null || ret[0] != p.name ) continue;
						
						for each( var ruleEvent:Array in rule.events )
						{
							if( ruleEvent[0] != e.type ) continue;

							var params:Array = [];
							for( var i:int=1; i<ruleEvent.length; ++i )
							{
								var val:Object = ruleEvent[i];
								if( val is Function ) val = val(THIS);
								params.push( p.name.replace( reg, val ) );
							}
							if( autoPipe && !(params[0] in autos) )
							{
								parent.addPipe(params[0], null);
								autos[ params[0] ] = params[0];
							}
							if( Oboe.flags.DEBUG && debugTraces ) trace( "Autofiring:", params[0] );
							shortSignal.apply( null, params ); 
						}
					}
				});
			}
			
			this.clips = null;
		}
		
		[slot] public function updateLabels():void
		{
			clips = new Dictionary();
			parent = this.getSigSlotParent();
			if( !parent ) return;
			var ret:Array = [];
			parent.message( "_getDisplayObject", ret );
			var asset:DisplayObject = ret[0];
			
			recurse( asset );
			
			clips = null;
		}
		
		private var clips:Dictionary;
		
		private function recurse( p:DisplayObject ):void
		{
			var c:int;
			/* * /
			if( p is MovieClip )
			{
				var frame:int;
				var mc:MovieClip = MovieClip(p);
				for( frame=0; frame<mc.totalFrames; frame++ ){
					mc.gotoAndStop( frame );
					for( c = 0; c<mc.numChildren; ++c )
					{
						recurse( mc.getChildAt(c) );
					}
				}
				mc.gotoAndPlay(0);
				return;
			}
			else /* */
			
			if( p is SimpleButton )
			{
				recurse( SimpleButton(p).downState );
				recurse( SimpleButton(p).upState );
				recurse( SimpleButton(p).hitTestState );
				recurse( SimpleButton(p).overState );
				return;
			}
			else if( p is DisplayObjectContainer )
			{
				var doc:DisplayObjectContainer = p as DisplayObjectContainer;
				for( c = 0; c<doc.numChildren; ++c )
				{
					recurse( doc.getChildAt(c) );
				}
			}
			
			if( p in clips ) return;
			clips[ p ] = p;
			
			if( p is TextField )
			{
				var ret:Object = this.labelRule.exec( p.name );
				if( ret == null || ret[0] != p.name ) return;
				var name:String = ret[1].substr(0,1).toLowerCase() + ret[1].substr(1);
				if( !(name in Oboe.flags) ) return;
				var val:String = Oboe.flags[ name ];
				if( val == TextField(p).text ) return;
				TextField(p).text = val;
					/*
					for each( var event:Array in rule.events )
					{
						var params:Array = [];
						for( var i:int=1; i<event.length; ++i )
						{
							var val:Object = event[i];
							if( val is Function ) val = val(this)
							params.push( p.name.replace( reg, val ) );
						}
						trace( params[0] );
						Prototype.addCallback( 
							p, 
							event[0], 
							function( params:Array ):void { 
								shortSignal.apply( null, params ); 
							},
							params
						);
						if( this.autoPipe ) parent.addPipe( params[ 0 ], null );
					}
					*/
			}
		}
	}
}
